package com.glodon.paas.document.storage.dao;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.glodon.paas.document.storage.NodeType;
import com.glodon.paas.document.storage.dao.mapper.StorageMapper;
import com.glodon.paas.document.storage.model.Node;
import com.glodon.paas.document.storage.model.NodeAssoc;
import com.glodon.paas.document.storage.model.Store;
import com.glodon.paas.document.storage.util.StorageUtil;

@Repository
public class StorageDAO {
    @Autowired
    private StorageMapper storageMapper;

    public void setStorageMapper(StorageMapper storageMapper) {
        this.storageMapper = storageMapper;
    }

    public Store getStoreByName(String name) {
        return storageMapper.getStoreByName(name);
    }

    public List<Node> getChildNodes(String parent) {
        List<NodeAssoc> nodeAssocs = storageMapper.getNodeAssoc(parent);
        List<Node> children = new ArrayList<Node>();
        for (NodeAssoc nodeAssoc : nodeAssocs) {
            children.add(nodeAssoc.getChild());
        }
        return children;
    }

    public NodeAssoc getNodeAssoc(String parent, String name) {
        return storageMapper.getNodeAssocByName(parent, name);
    }

    public void addChildNode(Node parent, Node child, String name) {
        if (child.getId() == null)
            child.setId(StorageUtil.generateUUID());
        if (child.getType() == NodeType.revision || child.getType() == NodeType.part) {
            long version = getNextVersion(parent);
            child.setVersion(version);
        }
        String storeId = parent.getStore().getId();
        Store store = new Store();
        store.setId(storeId);
        child.setStore(store);
        storageMapper.insertNode(child);
        addChildAssoc(parent.getId(), child.getId(), name);
    }

    private long getNextVersion(Node file) {
        if (file.getType() != NodeType.file)
            throw new RuntimeException("add revision on node, but node is not file");
        return storageMapper.getNextVersion(file.getId());
    }

    public Node getChildNode(String id, String name) {
        NodeAssoc nodeAssoc = storageMapper.getNodeAssocByName(id, name);
        return nodeAssoc == null ? null : nodeAssoc.getChild();
    }

    public void updateNode(Node node) {
        storageMapper.updateNode(node);
    }

    public void addChildAssoc(String parent, String child, String name) {
        String childAssocId = StorageUtil.generateUUID();
        NodeAssoc nodeAssoc = new NodeAssoc(childAssocId, parent, child, name);
        storageMapper.insertChildAssoc(nodeAssoc);
    }

    public void deleteNode(Node parent, Node child, String name) {
        NodeAssoc nodeAssoc = storageMapper.getNodeAssocByName(parent.getId(), name);
        storageMapper.deleteNodeAssoc(nodeAssoc);
        deleteNodeWhenNoParent(child);
    }

    private void deleteNodeWhenNoParent(Node node) {
        List<NodeAssoc> parentNodeAssocs = storageMapper.getNodeAssocByChild(node.getId());
        if (parentNodeAssocs.isEmpty()) {
            List<NodeAssoc> nodeAssocs = storageMapper.getNodeAssoc(node.getId());
            for (NodeAssoc nodeAssoc : nodeAssocs) {
                storageMapper.deleteNodeAssoc(nodeAssoc);
            }
            storageMapper.deleteNode(node);
            for (NodeAssoc nodeAssoc : nodeAssocs) {
                deleteNodeWhenNoParent(nodeAssoc.getChild());
            }
        }
    }

    public Node getNode(String id) {
        return storageMapper.getNode(id);
    }

    public List<Store> getStores() {
        return storageMapper.getStores();
    }

    public void addStorage(String name, String directory) {
        Store store = new Store();
        store.setId(StorageUtil.generateUUID());
        store.setName(name);
        store.setDirectory(directory);
        storageMapper.insertStore(store);
        Node root = new Node();
        root.setId(StorageUtil.generateUUID());
        root.setStore(store);
        root.setType(NodeType.folder);
        storageMapper.insertNode(root);
        store.setRoot(root);
        storageMapper.updateStoreRoot(store);
    }
}
